<template>
  <el-row>
    <div class="mb-2 ml-4">
      <el-radio-group
        v-model="isWorkstationSelected"
        :disabled="isPageDisabled"
      >
        <el-radio :value="true" size="large">{{
          $t('pageFirmware.form.updateFirmware.workstation')
        }}</el-radio>
        <el-radio
          :value="false"
          size="large"
          :disabled="!isTftpUploadAvailable"
          >{{ $t('pageFirmware.form.updateFirmware.tftpServer') }}</el-radio
        >
      </el-radio-group>
    </div>
  </el-row>
  <div v-if="isWorkstationSelected === true" class="pl-3">
    <h2 class="font-semibold mb-3">
      {{ $t('pageFirmware.form.updateFirmware.imageFile') }}
    </h2>
    <el-form :disabled="isPageDisabled">
      <el-upload
        ref="upload"
        class="upload-demo"
        :limit="1"
        :on-exceed="handleExceed"
        :auto-upload="false"
        v-model:file-list="file"
      >
        <template #trigger>
          <el-button type="primary">{{
            $t('global.fileUpload.browseText')
          }}</el-button>
        </template>
        <el-button class="ml-3" type="success" @click="submitUpload">
          {{ $t('pageFirmware.form.updateFirmware.startUpdate') }}
        </el-button>
        <template #tip>
          <div class="el-upload__tip text-red">
            {{ $t('pageFirmware.form.updateFirmware.exceedTip') }}
          </div>
        </template>
      </el-upload>
    </el-form>
  </div>
  <div v-else class="pl-3">
    <h2 class="font-semibold mb-3">
      {{ $t('pageFirmware.form.updateFirmware.fileAddress') }}
    </h2>
    <el-form
      ref="formRef"
      :model="form"
      :rules="rules"
      :disabled="isPageDisabled"
    >
      <div class="mb-3">
        <el-form-item prop="tftpFileAddress">
          <el-input
            v-model="form.tftpFileAddress"
            style="width: 240px"
            placeholder="Please input file address"
          />
        </el-form-item>
        <el-button type="success" @click="submitUpload">
          {{ $t('pageFirmware.form.updateFirmware.startUpdate') }}
        </el-button>
      </div>
    </el-form>
  </div>
  <el-alert
    v-if="isServerPowerOffRequired && !isServerOff"
    :title="$t('pageFirmware.alert.serverMustBePoweredOffToUpdateFirmware')"
    type="warning"
  />
  <el-dialog
    v-model="visible1"
    :show-close="false"
    width="500"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
  >
    <template #header="{ titleId, titleClass }">
      <div>
        <h4 :id="titleId" :class="titleClass">{{ updatingTitle }}</h4>
      </div>
    </template>
    <el-progress :percentage="updateProgress" />
  </el-dialog>
  <el-dialog
    v-model="visible2"
    :show-close="false"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    width="500"
  >
    <template #header="{ titleId, titleClass }">
      <div>
        <h4 :id="titleId" :class="titleClass">
          {{ $t('pageFirmware.modal.bmcUpdatingTitle') }}
        </h4>
      </div>
    </template>
    <el-progress :percentage="100" status="exception" :indeterminate="true">
      <el-button text>{{
        $t('pageFirmware.modal.updating').split(':')[0]
      }}</el-button>
    </el-progress>
  </el-dialog>
</template>

<script setup lang="ts">
import { useFirmwareStore } from '@/store/Settings/FirmwareStore';
import { genFileId } from 'element-plus';
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
import useVuelidateTool from '@/hooks/useVuelidateTool';
import { useI18n } from 'vue-i18n';
import { useControlStore } from '@/store/Settings/ControlStore';

const i18n = useI18n();

const { useVuelidate, requiredIf, elementInputValidator } = useVuelidateTool();

defineProps<{ isPageDisabled: boolean; isServerOff: boolean }>();
const firmwareStore = useFirmwareStore();
const controlStore = useControlStore();
const isWorkstationSelected = ref(true);
const upload = ref<UploadInstance>();
const file = ref();
const isServerPowerOffRequired =
  import.meta.env.VUE_APP_SERVER_OFF_REQUIRED === 'true';
const updateFirmware = ref({} as { [index: string]: any });
const isSingleFileUploadEnabled = computed(() => {
  return firmwareStore.isSingleFileUploadEnabled;
});
const visible1 = ref(false);
const visible2 = ref(false);

const form = reactive({ tftpFileAddress: null });
const v$ = useVuelidate(
  {
    tftpFileAddress: {
      required: requiredIf(() => {
        return !isWorkstationSelected.value;
      }),
    },
  },
  form,
);
const rules = computed(() => {
  return {
    tftpFileAddress: {
      trigger: 'change',
      validator: elementInputValidator.value(v$, 'tftpFileAddress'),
    },
  };
});

onMounted(() => {
  firmwareStore.getUpdateServiceSettings();
});

const isTftpUploadAvailable = computed(() => {
  return firmwareStore.isTftpUploadAvailable;
});
const runningBmc = computed(() => {
  return firmwareStore.activeBmcFirmware;
});
const hostFirmware = computed(() => {
  return firmwareStore.hostFirmware;
});
const updatingTitle = computed(() => {
  let updateTitle = {
    updating: i18n.t('pageFirmware.modal.updating'),
    firmtype: updateFirmware.value.type,
  };
  return updateTitle.firmtype
    ? updateTitle.updating + updateTitle.firmtype
    : updateTitle.updating.split(':')[0];
});

const runningBmcVersion = computed(() => {
  return runningBmc.value?.version || '--';
});

const updateProgress = computed(() => {
  return firmwareStore.updateProgress;
});

watch(isWorkstationSelected, function () {
  v$.value.$reset();
  file.value = [];
  form.tftpFileAddress = null;
});

const handleExceed: UploadProps['onExceed'] = (files) => {
  upload.value!.clearFiles();
  const file = files[0] as UploadRawFile;
  file.uid = genFileId();
  upload.value!.handleStart(file);
};

const submitUpload = () => {
  v$.value.$touch();
  if (v$.value.$invalid) return;
  ElMessageBox.confirm(
    isSingleFileUploadEnabled.value
      ? i18n.t('pageFirmware.modal.updateFirmwareInfo') +
          i18n.t('pageFirmware.modal.updateFirmwareInfo2', {
            running: runningBmcVersion,
          }) +
          i18n.t('pageFirmware.modal.updateFirmwareInfo3')
      : i18n.t('pageFirmware.modal.updateFirmwareInfoDefault'),
    i18n.t('pageFirmware.sectionTitleUpdateFirmware'),
    {
      confirmButtonText: 'OK',
      cancelButtonText: 'Cancel',
      type: 'warning',
    },
  )
    .then(() => {
      update();
    })
    .catch(() => {});
};

const update = async () => {
  // Init some state values in firmware.
  visible1.value = true;

  ElNotification({
    title: i18n.t('pageFirmware.toast.updateStarted'),
    message: i18n.t('pageFirmware.toast.updateStartedMessage'),
    type: 'info',
  });
  // Used for resetting basicFirmware informations.
  await firmwareStore.getFirmwareInventory();

  /* Clear redundant Hostfirmware. this can be used in special cases such as
      getting power off in updating. */
  if (hostFirmware.value.length > 1) {
    let redundantHostFirmware = hostFirmware.value.filter(
      (item: { id: any }) => {
        return item.id != firmwareStore.hostActiveFirmwareId;
      },
    );
    firmwareStore.setHostFirmware(
      hostFirmware.value.filter((item: { id: any }) => {
        return item.id == firmwareStore.hostActiveFirmwareId;
      }),
    );
    for (let item of redundantHostFirmware) {
      firmwareStore.deleteBrokenFirmware(item.id);
    }
  }
  // It is time to verify update status.
  const timerId = setTimeout(() => {
    visible1.value = false;
    ElNotification({
      title: i18n.t('pageFirmware.toast.verifyUpdate'),
      message: i18n.t('pageFirmware.toast.verifyUpdateMessage'),
      type: 'info',
    });
  }, 360000);

  // this flag used for validating while post successfully or not.
  let updateFlag = true;
  let updatingFirmware: {
    type?: string;
    taskUrl?: string;
  } = {};
  if (isWorkstationSelected.value) {
    updatingFirmware = await dispatchWorkstationUpload(timerId)
      .then((data) => data as any)
      .catch(() => {
        updateFlag = false;
      });
  } else {
    updatingFirmware = await dispatchTftpUpload(timerId)
      .then((data) => data as any)
      .catch(() => {
        updateFlag = false;
      });
  }
  if (!updateFlag) return;
  // Deliver to FirmwareModalUpdateFirmware.vue
  updateFirmware.value = updatingFirmware;
  // Used for compare the version of the active firmware.
  // Watch progress in real-time and stop watching when lose progress value.
  if (
    updatingFirmware['type'] == 'bios' ||
    updatingFirmware['type'] == 'cpld'
  ) {
    const timerId2 = setInterval(async () => {
      await firmwareStore
        .getUpdateinfo(updatingFirmware.taskUrl as string)
        .then(async ({ TaskState }: { TaskState: string }) => {
          if (TaskState == 'Completed' || TaskState == 'Exception')
            clearTimeout(timerId2);
          // Get the version of the active bios firmware.
          if (TaskState == 'Completed') {
            firmwareStore.setUpdateProgress(100);
            setTimeout(async () => {
              clearTimeout(timerId);
              visible1.value = false;
              ElNotification({
                message: i18n.t('pageFirmware.toast.updateSuccessfully'),
                type: 'success',
              });
              ElNotification({
                message: i18n.t('pageFirmware.toast.verifyUpdateMessage'),
                type: 'info',
              });
              // Reset basic states.
              firmwareStore.setUpdateProgress(0);
              firmwareStore.getFirmwareInventory();
            }, 5000);
          }
          if (TaskState == 'Exception') {
            clearTimeout(timerId);
            visible1.value = false;
            ElNotification({
              message: i18n.t('pageFirmware.toast.errorUpdate'),
              type: 'error',
            });
            firmwareStore.setUpdateProgress(0);
            firmwareStore.getFirmwareInventory();
          }
        });
    }, 2000);
  }
  if (updatingFirmware['type'] == 'bmc') {
    visible1.value = false;
    visible2.value = true;
    bmcUpdate(updatingFirmware.taskUrl as string);
  }
};
const bmcUpdate = (taskUrl: string) => {
  firmwareStore
    .getUpdateinfo(taskUrl)
    .then(
      ({
        TaskState,
        TaskStatus,
      }: {
        TaskState: string;
        TaskStatus: string;
      }) => {
        if (TaskState == 'Completed' && TaskStatus == 'OK') {
          controlStore.rebootBmc();
          ElNotification({
            message: i18n.t('pageFirmware.toast.UpdatingRebootBmc'),
            type: 'success',
          });
        } else if (TaskState == 'Stopping' && TaskStatus == 'OK') {
          visible2.value = false;
          ElNotification({
            message: i18n.t('pageFirmware.toast.errorStop'),
            type: 'error',
          });
        } else if (TaskState == 'Exception' && TaskStatus == 'Warning') {
          ElNotification({
            message: i18n.t('pageFirmware.toast.errorChecked'),
            type: 'error',
          });
        } else if (TaskState == 'Cancelled' && TaskStatus == 'Warning') {
          ElNotification({
            message: i18n.t('pageFirmware.toast.TimeoutCancelled'),
            type: 'error',
          });
        } else if (TaskState == 'Running' && TaskStatus == 'OK') {
          bmcUpdate(taskUrl);
        }
      },
    );
};
const dispatchWorkstationUpload = async (timerId: number | undefined) => {
  return await firmwareStore
    .uploadFirmware(file.value[0])
    .then((data) => data)
    .catch(({ message }: { message: string }) => {
      visible1.value = false;
      ElNotification({
        message,
        type: 'error',
      });
      clearTimeout(timerId);
      throw 'error';
    });
};
const dispatchTftpUpload = async (timerId: number | undefined) => {
  return await firmwareStore
    .uploadFirmwareTFTP(form.tftpFileAddress)
    .then((data) => data)
    .catch(({ message }: { message: string }) => {
      visible1.value = false;
      ElNotification({
        message,
        type: 'error',
      });
      clearTimeout(timerId);
      throw 'error';
    });
};
</script>
